package com.kehutong.auth.service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.kehutong.auth.entity.AccountLoginLog;
import com.kehutong.auth.entity.UserSearch;
import com.kehutong.auth.enums.LoginMethod;
import com.kehutong.auth.util.IPSeeker;
import com.kehutong.common.IpUtils;
import com.kehutong.common.constant.CommonConstant;
import com.kehutong.common.entity.Root;
import com.kehutong.common.permission.UserLoginInfo;
import org.coraframework.authz.AccessToken;
import org.coraframework.authz.AuthenticationInfo;
import org.coraframework.authz.Subject;
import org.coraframework.authz.SubjectUtil;
import org.coraframework.authz.exception.AuthenticationException;
import org.coraframework.authz.exception.IncorrectCredentialsException;
import org.coraframework.authz.exception.NotPermissionException;
import org.coraframework.authz.exception.UnknownAccountException;
import org.coraframework.authz.util.CookieUtils;
import org.coraframework.inject.Inject;
import org.coraframework.inject.Singleton;
import org.coraframework.json.JSONArray;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.connection.ThreadLocalCache;
import org.coraframework.orm.util.Enums;
import org.coraframework.util.CallBack;
import org.coraframework.util.Objects;
import org.coraframework.util.time.TimeUtil;

import com.kehutong.auth.entity.User;
import com.kehutong.auth.enums.AccountStatus;
import com.kehutong.auth.enums.LoginType;
import com.kehutong.common.DoveClient;
import com.kehutong.common.enums.Gender;
import com.kehutong.common.enums.UserType;
import com.kehutong.common.errcode.Result;
import com.kehutong.common.isp.TencentSms;
import com.kehutong.common.util.Token;

@Singleton
public class LoginEmployeeService {

    private static final Logger logger = LoggerFactory.getLogger(LoginEmployeeService.class);

    private static final String SUCCESS = "登录成功";

    @Inject
    private DoveClient doveClient;
    @Inject
    private TencentSms ispService;
    @Inject
    private JdbcSession jdbcSession;


    public Object isLogin(Token token, String corpId, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        logger.debug("corpId:{}", corpId);
        if (Objects.isNull(token)) {
            return Result.success();
        }

        if (Objects.isEmpty(corpId)) {
            boolean result=flushToken(req, resp);
            if(!result){
                return Result.success();
            }
            return Result.success(token);
        }

        if (corpId.equals(token.getCorpid())) {
            flushToken(req, resp);
            return Result.success(token);
        }

        if (corpId.equals(token.getSuiteId())) {
            flushToken(req, resp);
            return Result.success(token);
        }

        onLogout(token.getSessionId());
        return Result.success();
    }

    // 密码登陆
    public Object logingByPass(HttpServletRequest req, String username, String password) throws Exception {
        return login(req, LoginType.EMPLOYEE_PASS, username, password, token -> {
            token.setUserType(UserType.system);
            // TODO 默认一个应用
            if (Objects.isEmpty(token.getSuiteId())) {
                token.setSuiteId("bc1778b972b342a5a9f7d2e34825dcf7");
            }
        });
    }

    // 企业oauth2.0授权登陆
    public Object loginByTencentWework(HttpServletRequest req, String suiteId, String code,UserType userType) throws Exception {
        //获取微信内建的企业信息(com.kehutong.wwout.webservice.EmployeeService)
        JSONObject rs = doveClient.post("/kht-bin/wwout/employee/getUserInfo", (http) -> {
            http.addParam("suiteId", suiteId);
            http.addParam("code", code);
        });

        if (rs.getIntValue("code") != 200) {
            return Result.error(0003, "登录初始化上下文信息失败");
        }

        JSONObject userInfo = rs.getJSONObject("data");

        String version=userInfo.getString("version");
        if(Objects.equal("1.0",version)){
            return authLogin(req, LoginType.AUTHOR, userInfo.getString("id"), "kehutong@2020", token -> {
                token.setUuid(userInfo.getString("id"));
                token.setCorpid(userInfo.getString("corpId"));
                token.setSuiteId(userInfo.getString("suiteId"));
                token.setUserId(userInfo.getString("userId"));
                token.setUserType(UserType.webwork);
                token.setCompanyName("客户通1.0适配Oauth登录");
                token.setUsername(userInfo.getString("name"));
                token.setHeadUrl(userInfo.getString("avatar"));
                token.setExtContent(userInfo.getString("extContent"));
                token.setVersion("1.0");
            });
        }

        //授权成功创建企业用户账号
        // createUser(userInfo);

        logger.debug("wxOutLogin==========wwloginByOauth :{}============", rs);
        return login(req, LoginType.EMPLOYEE_AUTHOR, userInfo.getString("id"), "kehutong@2020", token -> {
            token.setCorpid(userInfo.getString("corpId"));
            token.setSuiteId(userInfo.getString("suiteId"));
            token.setUserId(userInfo.getString("userId"));
            token.setUserType(userType);
            token.setVersion("2.0");
        });
    }

    // 企业扫码登陆
    public Object wwloginByTencentWeworkQrcode(HttpServletRequest req, String corpId, String authCode) throws Exception {
        //获取微信内建的企业信息
        JSONObject rs = doveClient.post("/kht-bin/wwout/employee/getLoginInfo", (http) -> {
            http.addParam("corpId", corpId);
            http.addParam("code", authCode);
        });

        if (rs.getIntValue("code") != 200) {
            return Result.error(0003, rs.getString("message"));
        }

        JSONObject userInfo = rs.getJSONObject("data");
        String version=userInfo.getString("version");
        if(Objects.equal("1.0",version)){
            return authLogin(req, LoginType.AUTHOR, userInfo.getString("id"), "kehutong@2020", token -> {
                token.setUuid(userInfo.getString("id"));
                token.setCorpid(userInfo.getString("corpId"));
                token.setSuiteId(userInfo.getString("suiteId"));
                token.setUserId(userInfo.getString("userId"));
                token.setUserType(UserType.webwork);
                token.setCompanyName("客户通1.0适配Oauth登录");
                token.setUsername(userInfo.getString("name"));
                token.setHeadUrl(userInfo.getString("avatar"));
                token.setExtContent(userInfo.getString("extContent"));
                token.setVersion("1.0");
            });
        }

        createUser(userInfo);

        return login(req, LoginType.EMPLOYEE_AUTHOR, userInfo.getString("id"), "kehutong@2020", token -> {
            token.setCorpid(userInfo.getString("corpId"));
            token.setSuiteId(userInfo.getString("suiteId"));
            token.setUserId(userInfo.getString("userId"));
            token.setUserType(UserType.webwork);
            token.setVersion("2.0");
        });
    }

    // 授权Auth2.0登陆处理
    protected Object authLogin(HttpServletRequest req, LoginType type, String username, String password, CallBack<Token> call) throws Exception {
        try {
            AuthenticationInfo authInfo = new AuthenticationInfo(username, password, type);
            AccessToken accessToken = SubjectUtil.getSubject().login(authInfo);
            logger.debug("LoginService==========authLogin result: accessToken :{}============", accessToken);
            Token token = Token.create();
            token.setSessionId(accessToken.getSessonId());
            call.call(token);
            onLogin(token);
            return token;
        } catch (NotPermissionException e) {
            logger.info("验证码验证失败");
            return Result.error(0002, "您输入的验证码不正确");
        } catch (UnknownAccountException | IncorrectCredentialsException e) {
            // UserUtils.increaseLoginErrorCount(username);
            return Result.error(0001, "您输入的帐号或密码有误");
        } catch (AuthenticationException e) {
            return Result.error(0, e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Result.error(0, e.getMessage());
        }
    }
    // 登陆处理
    protected Object login(HttpServletRequest req, LoginType type, String username, String password, CallBack<Token> call) throws Exception {
        String message = SUCCESS;
        try {
            AuthenticationInfo authInfo = new AuthenticationInfo(username, password, type);
            AccessToken accessToken = SubjectUtil.getSubject().login(authInfo);
            logger.debug("LoginService==========baseLogin result: accessToken :{}============", accessToken);

            User user = jdbcSession.findOne(User.class)
                    .eq("id", accessToken.getUserId())
                    .eq("deleted", false)
                    .exe();
            username = user.getLogin_name();

            // 公司信息模式  0.第三方 1.内建
            String cropPattern="0";
            if (!user.getId().equals(Root.ROOT_ID)){
                JSONObject corpResult = doveClient.post("/admin/service/corp/get", (http) -> {
                    http.addParam("id", user.getCorpNo());
                });
                if (corpResult.getIntValue("code") == 200) {
                    JSONObject data = corpResult.getJSONObject("data");
                    if (data.getBoolean("deleted")){
                        message = "您的企业已经解散";
                        return Result.error(50102, message);
                    }
                    JSONObject cropPatternJson=data.getJSONObject("pattern");
                    cropPattern=cropPatternJson.getString("id");

                }
            }

            Token token = Token.create();
            flushIP(user, req.getRemoteAddr());
            token.setSessionId(accessToken.getSessonId());
            token.setCompanyNo(user.getCorpNo());
            token.setCompanyName(user.getCorpName());
            token.setUuid(user.getId());
            token.setUsername(user.getName());
            //token.setPermissions(UserUtils.getPermissions(user, false).keySet());
            token.setMobile(user.getMobile());
            token.setHeadUrl(user.getAvatar());
            token.setRoles(user.getRoles());
            token.setUserCreateTime(user.getCreateTime().getTime());
            token.setRemindTime(Objects.nonNull(user.getRemindTime()) ? user.getRemindTime().getTime() : null);
            token.setCropPattern(cropPattern);
            call.call(token);

            // 设置登录用户
            UserLoginInfo userLoginInfo = new UserLoginInfo();
            userLoginInfo.setUserId(accessToken.getUserId());
            userLoginInfo.setUsername(user.getName());
            userLoginInfo.setRoles(user.getRoles());

            if (Objects.isEmpty(user.getCorpNo())) {
                token.setDepartments(Collections.emptyList());
            }else{
                JSONObject employeeResult = doveClient.post("/basic/employee/service/get", (http) -> {
                    http.addHeader("companyNo", user.getCorpNo());
                    http.addParam("id", user.getId());
                });
                if(Objects.nonNull(employeeResult)){
                    if (employeeResult.getIntValue("code") != 200) {
                        return Result.error(0, employeeResult.getString("message"));
                    }
                    JSONObject employee = employeeResult.getJSONObject("data");

                    //只有后台登录
                    if(Objects.equal(UserType.system.getId(),token.getUserType().getId())){
                        if(!employee.getBoolean("enable") && !user.getRoles().contains("5")){
                            message = "无权限登陆，请联系相关人员开启权限";
                            return Result.error(0, message);
                        }
                    }

                    // 设置身份
                    JSONObject identity = employee.getJSONObject("identity");
                    if (identity == null) {
                        userLoginInfo.setIdentity(0);
                    }
                    else {
                        userLoginInfo.setIdentity(identity.getIntValue("id"));
                    }

                    // 设置部门
                    JSONArray department = employee.getJSONArray("department");
                    List<String> departmentList = new ArrayList<>();
                    Set<String> departmentSet = new HashSet<>();
                    if(Objects.nonEmpty(department)){
                        for (int i = 0; i < department.size(); i++) {
                            departmentList.add(department.getString(i));
                            departmentSet.add(department.getString(i));
                        }
                    }

                    // 设置指定部门
                    JSONArray manageDepartmentNo = employee.getJSONArray("dutyDepartment");
                    Set<String> manageDepartmentSet = new HashSet<>();
                    if(Objects.nonEmpty(manageDepartmentNo)){
                        for (int i = 0; i < manageDepartmentNo.size(); i++) {
                            String each = manageDepartmentNo.getString(i);
                            if (!departmentList.contains(each)) {
                                departmentList.add(each);
                            }
                            manageDepartmentSet.add(each);
                        }
                    }

                    // 获取角色信息里指定的部门信息
                    List<String> roleDepartmentNos = new ArrayList<>();
                    getRoleinfo(token, userLoginInfo, roleDepartmentNos);

                    if(Objects.nonEmpty(roleDepartmentNos)){
                        for (String each : roleDepartmentNos) {
                            if (!departmentList.contains(each)) {
                                departmentList.add(each);
                            }
                        }
                        manageDepartmentSet.addAll(roleDepartmentNos);
                    }
                    userLoginInfo.setManageDepartmentNo(manageDepartmentSet);

                    userLoginInfo.setDepartmentNo(departmentSet);
                    token.setDepartments(departmentList);
                }else{
                    token.setDepartments(Collections.emptyList());
                }
            }

            // 保存用户信息
            Subject subject = SubjectUtil.getCache(accessToken.getSessonId());
            subject.setAttr(CommonConstant.CURRENT_USER_INFO + ":" + subject.getUserId(), userLoginInfo);
            onLogin(token);

            initUserSearch(user, userLoginInfo);

            return token;
        } catch (NotPermissionException e) {
            message = "您输入的验证码不正确";
            logger.info(message);
            return Result.error(0002, message);
        } catch (UnknownAccountException e) {
            // UserUtils.increaseLoginErrorCount(username);
            message = "您输入的帐号或密码有误";
            return Result.error(0001, message);
        } catch (IncorrectCredentialsException e) {
            message = "您输入的帐号或密码有误";
            return Result.error(0001, message);
        } catch (AuthenticationException e) {
            message = e.getMessage();
            return Result.error(0, e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            message = e.getMessage();
            return Result.error(0, e.getMessage());
        } finally {
            AccountLoginLog log = Pool.newInstance(AccountLoginLog.class);
            log.setAccountNo(username);
//            String ip = IPUtil.getRealIp();
            String ip = IpUtils.getAddr(req);
            log.setIp(ip);
            IPSeeker ipSeeker = new IPSeeker();
            if(null != ipSeeker){
                log.setAddress(ipSeeker.getIPLocation(ip).getCountry());
            }
            log.setLoginMethod(LoginMethod.WEB_PROGRAM);
            log.setRemark(message);
            jdbcSession.insert(log);
        }
    }
    // 登陆后续事件处理
    void onLogin(Token token) throws Exception {
        JSONObject rs = doveClient.post("/kht-bin/session/service/login", http -> {
            http.setBody(token.toJSON().toJSONString());
        });

        logger.debug("LoginService.login result:{}", token);
        if (rs.getInteger("code") != 200) {
            throw new AuthenticationException("login fail: " + rs.getString("message"));
        }
    }

    // 登出后续事件处理
    public void onLogout(String id) throws Exception {
        doveClient.post("/kht-bin/session/service/logout", http -> {
            http.addParam("sessionId", id);
        });
    }

    public boolean flushToken(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        AccessToken token = SubjectUtil.getSubject().getToken();
        if (token == null) {
            return false;
        }

        if (TimeUtil.timeMillis() < token.getFlushTime().getTime()) {
            token.flushTime();
            CookieUtils.setCookie(req, resp, token.getSessonId());
            return true;
        }

        return false;
    }

    private User createUser(JSONObject jsonObject) {
        String id =  jsonObject.getString("id");
        User user = Pool.get(User.class, id);

        if (Objects.isNull(user)) {
            user = Pool.newInstance(User.class, id);
            setProperties(user, jsonObject);
            jdbcSession.insert(user);
//    	} else {
//    		setProperties(user, jsonObject);
//    		jdbcSession.updateById(user);
        }

        return user;
    }

    private void setProperties(User user, JSONObject jsonObject) {
        user.setDeleted(false);
        user.setCorpNo(jsonObject.getString("corpNo"));
        user.setCorpName(jsonObject.getString("corpName"));
        user.setAvatar(jsonObject.getString("avatar"));
        user.setName(jsonObject.getString("name"));
        user.setUserId(jsonObject.getString("userId"));
        user.setLogin_name(jsonObject.getString("login_name"));
        user.setGender(Enums.getEnum(Gender.class, jsonObject.getString("gender")));
        user.setStatus(AccountStatus.ACTIVE);

        Set<String> roles = new HashSet<>();
        roles.add("4");
        user.setRoles(roles);
        user.setPassword(Subject.encrypt("kehutong@2020"));
    }

    public Object logPage(Token token, JSONObject jsonObject){
       User user = Pool.get(User.class, token.getUuid());

       return jdbcSession.findPage(AccountLoginLog.class)
               .eq("accountNo", user.getLogin_name())
               .order("createTime", "DESC")
               .page(jsonObject)
               .exe();
    }

    private void initUserSearch(User user, UserLoginInfo userLoginInfo) throws Exception {
        if(Objects.isEmpty(user.getCorpNo())){
            return;
        }

        ThreadLocalCache.current().setCorpId(user.getCorpNo());
        UserSearch userSearch = Pool.get(UserSearch.class, user.getId());
        if(Objects.isNull(userSearch)){
            userSearch = Pool.newInstance(UserSearch.class, user.getId());
            // 获取有权限查询到的小区信息
            JSONObject result = doveClient.post("/basic/community/service/permissionList", http -> {
                JSONObject body = new JSONObject();
                body.put("userId",userLoginInfo.getUserId());
                body.put("departmentNo",userLoginInfo.getDepartmentNo());
                body.put("manageDepartmentNo",userLoginInfo.getManageDepartmentNo());
                body.put("identity",userLoginInfo.getIdentity());
                body.put("roles",userLoginInfo.getRoles());
                body.put("allData", userLoginInfo.isAllData());
                http.setBody(body.toString());
            });
            JSONArray data = result.getJSONArray("data");
            if (Objects.nonEmpty(data)) {
                JSONObject jsonObject = (JSONObject) data.get(0);
                userSearch.setSearch(jsonObject.getString("name"));
            }
            jdbcSession.insert(userSearch);
        }
    }

    public void flushIP(User user, String loginIp) {
        user.setLogin_ip(loginIp);
        user.setLogin_date(LocalDateTime.now());
        jdbcSession.updateById(user);
    }

    private void getRoleinfo(Token token,  UserLoginInfo userLoginInfo, List<String> roleDepartmentNos) throws Exception {
        if(Objects.isEmpty(token.getRoles())){
            return;
        }

        JSONObject result = doveClient.post("/admin/service/role/list", (http) -> {
            JSONObject body = new JSONObject();
            body.put("ids", new ArrayList<>(token.getRoles()));
            http.setBody(body.toString());
        });

        if(Objects.nonEmpty(result)) {
            JSONArray data = result.getJSONArray("data");
            if(Objects.nonEmpty(data)){
                for (int i = 0; i < data.size(); i++) {
                    JSONObject json = data.getJSONObject(i);
                    String data_scope = json.getString("data_scope");
                    if(data_scope.equals("3")) {
                        JSONArray array = json.getJSONArray("manageDepartment");
                        for (int j = 0; j < array.size(); j++) {
                            String id = array.getString(j);
                            roleDepartmentNos.add(id);
                        }
                    }
                }

                for (int i = 0; i < data.size(); i++) {
                    JSONObject json = data.getJSONObject(i);
                    String data_scope = json.getString("data_scope");
                    if(data_scope.equals("1")) {
                        userLoginInfo.setAllData(true);
                    }
                    if(json.getBoolean("newsletterState")){
                        token.setNewsletterState(true);
                    }
                    if(json.getBoolean("settingState")){
                        token.setSettingState(true);
                    }
                    if(json.getBoolean("businessState")){
                        token.setBusinessState(true);
                    }
                    if(json.getBoolean("frontPageState")){
                        token.setFrontPageState(true);
                    }
                }
            }
        }
    }
}